#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAX_LEN (1*1024*1024)
#define POSITIVE_HEIGHT (1)

/*12Bytes*/
typedef struct                       /**** BMP file header structure ****/
{
    unsigned int   bfSize;           /* Size of file */
    unsigned short bfReserved1;      /* Reserved */
    unsigned short bfReserved2;      /* ... */
    unsigned int   bfOffBits;        /* Offset to bitmap data */
}BITMAPFILEHEADER;

/*40Bytes*/
typedef struct                       /**** BMP file info structure ****/
{
    unsigned int   biSize;           /* Size of info header */
    int            biWidth;          /* Width of image */
    int            biHeight;         /* Height of image */
    unsigned short biPlanes;         /* Number of color planes */
    unsigned short biBitCount;       /* Number of bits per pixel */
    unsigned int   biCompression;    /* Type of compression to use */
    unsigned int   biSizeImage;      /* Size of image data */
    int            biXPelsPerMeter;  /* X pixels per meter */
    int            biYPelsPerMeter;  /* Y pixels per meter */
    unsigned int   biClrUsed;        /* Number of colors used */
    unsigned int   biClrImportant;   /* Number of important colors */
}BITMAPINFOHEADER;

int simplest_rgb24_to_bmp(const char* rgb24Path, int w, int h, const char* bmpPath)
{
    int s32Ret = 0;
    int fd_ori = -1;
    int fd_bmp = -1;
    int headerSize = 0;
    int i = 0;//for circle
    int j = 0;//for circle
    unsigned char temp = 0;

    unsigned char readBuff[MAX_LEN] = {'\0'};
    memset(readBuff, 0, sizeof(readBuff));

#ifdef POSITIVE_HEIGHT
    unsigned char readBuff4Ph[MAX_LEN] = {'\0'};
    memset(readBuff4Ph, 0, sizeof(readBuff4Ph));
#endif

    char bfType[2] = {'B', 'M'};

    BITMAPFILEHEADER myHead;
    BITMAPINFOHEADER myHeadInfo;
    memset(&myHead, 0, sizeof(myHead));
    memset(&myHeadInfo, 0, sizeof(myHeadInfo));
    printf("sizeof(myHead) = %d\n", sizeof(myHead));
    printf("sizeof(myHeadInfo) = %d\n", sizeof(myHeadInfo));

    /*myHead*/
    headerSize = sizeof(bfType) + sizeof(myHead) + sizeof(myHeadInfo);
    myHead.bfSize = headerSize + w*h*3;
    myHead.bfOffBits = headerSize;

    /*myHeadInfo*/
    myHeadInfo.biSize = sizeof(myHeadInfo);
    myHeadInfo.biWidth = w;

#ifndef POSITIVE_HEIGHT
    myHeadInfo.biHeight = -1 * h;
#else
    myHeadInfo.biHeight = h;
#endif

    myHeadInfo.biPlanes = 1;
    myHeadInfo.biBitCount = 24;
    myHeadInfo.biSizeImage = w*h*3;

    /*open files*/
    fd_ori = open(rgb24Path, O_RDONLY);
    if(fd_ori < 0)
    {
        printf("open rgb24 failed!\n");
        return -1;
    }
    printf("open rgb24 success!\n");

    fd_bmp = open(bmpPath, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 777);
    if(fd_bmp < 0)
    {
        printf("open bmp failed!\n");
        close(fd_ori);
        return -1;
    }
    printf("open bmp success!\n");

    /*read*/
    memset(readBuff, 0, sizeof(readBuff));
    s32Ret = read(fd_ori, readBuff, sizeof(readBuff));
    if((s32Ret < 0) || (s32Ret != w*h*3))
    {
        printf("read RGB file failed!\n");
        close(fd_bmp);
        close(fd_ori);
        return -1;
    }
    printf("read RGB file success!\n");

    /*change R-G-B to B-G-R*/
    for(i = 0; i < (w*h); i++)
    {
        temp = *(readBuff + i*3);
        *(readBuff + i*3) = *(readBuff + i*3 + 2);
        *(readBuff + i*3 + 2) = temp;
    }

/*positive height storage sequence:left-right down-up*/
#ifdef POSITIVE_HEIGHT
    for(i = (h - 1), j = 0; i >= 0; i--, j++)
    {
        memcpy(readBuff4Ph + j*w*3, readBuff + i*w*3, w*3);
    }
#endif

    /*write-4 parts*/
    s32Ret = write(fd_bmp, bfType, sizeof(bfType));
    if(s32Ret < 0)
    {
        printf("write bfType failed!\n");
        close(fd_bmp);
        close(fd_ori);
        return -1;
    }
    s32Ret = write(fd_bmp, &myHead, sizeof(myHead));
    if(s32Ret < 0)
    {
        printf("write myHead failed!\n");
        close(fd_bmp);
        close(fd_ori);
        return -1;
    }
    s32Ret = write(fd_bmp, &myHeadInfo, sizeof(myHeadInfo));
    if(s32Ret < 0)
    {
        printf("write myHeadInfo failed!\n");
        close(fd_bmp);
        close(fd_ori);
        return -1;
    }
#ifdef POSITIVE_HEIGHT
    s32Ret = write(fd_bmp, readBuff4Ph, w*h*3);
    if(s32Ret < 0)
    {
        printf("write readBuff4Ph failed!\n");
        close(fd_bmp);
        close(fd_ori);
        return -1;
    }
    printf("write readBuff4Ph success!\n");
#else
    s32Ret = write(fd_bmp, readBuff, w*h*3);
    if(s32Ret < 0)
    {
        printf("write readBuff failed!\n");
        close(fd_bmp);
        close(fd_ori);
        return -1;
    }
    printf("write readBuff success!\n");
#endif

    close(fd_bmp);
    close(fd_ori);
    return 0;
}
